package com.bookshop.interceptor;

import com.bookshop.aspect.exception.CustomException;
import com.bookshop.configuration.BloomFilterManager;
import com.bookshop.util.RequestUtil;
import com.google.common.hash.BloomFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 * @author g2l
 * @create 2024-01-16 16:29
 */
@Slf4j
@Component
public class CurrentLimiterInterceptor implements HandlerInterceptor {

    @Resource
    private Environment environment;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private BloomFilterManager bloomFilterManager;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 获取ip
        String ip = RequestUtil.getIPAddress(request);

        // 这里理论上应该全部封禁，直接避免除了前端以外的所有ip的访问
        if("0:0:0:0:0:0:0:1".equals(ip) || "127.0.0.1".equals(ip)){
            log.info("本地访问，直接放行" + ip);
            return true;
        }

        // 去布隆过滤器里面查是否有此ip
        if (bloomFilterManager.mightContain(ip)){
            log.info("已加入布隆过滤器" + ip);
            // 如果在黑名单，直接拒绝访问
            Long expire = redisTemplate.getExpire("out!" + ip);
            // -2代表key不存在，-1代表该key永久存在，这里不会存在key永久存在的情况
            if (expire != -2){
                log.info("在黑名单中" + ip);
                // 刷新黑名单时间
                redisTemplate.opsForValue().set("out!" + ip, "", Long.parseLong(environment.getProperty("pass.ban")),
                        TimeUnit.SECONDS);
                throw new CustomException("访问过于频繁");
            }

            // 不在黑名单中，则设置一个自增值，不存在时redis会自动创建，初始化为1
            Long increment = redisTemplate.opsForValue().increment(ip, 1);
            redisTemplate.expire(ip, Long.parseLong(environment.getProperty("pass.seconds")),
                    TimeUnit.SECONDS); // 设定Seconds秒（例如3秒）的检测期

            log.info("访问了" + increment + "次" + ip);

            // 访问过于频繁，加入黑名单
            if (increment >= Long.parseLong(environment.getProperty("pass.times"))){
                log.info("被加入黑名单" + ip);
                redisTemplate.opsForValue().set("out!" + ip, "", Long.parseLong(environment.getProperty("pass.ban")),
                        TimeUnit.SECONDS);
                throw new CustomException("访问过于频繁");
            }

            // 未加入黑名单，计数并放行
            return true;
        }else {
            log.info("未加入布隆过滤器" + ip);
            // 没有则加入
            bloomFilterManager.put(ip);
        }

        return true;
    }
}
